;(in-package "ACL2")
(include-book "data-structures/structures" :dir :system)

(include-book "frame-stepper")
(include-book "world")
(include-book "rule-creator")
(include-book "command-parser")

(include-book "state-writer")
(include-book "hacking/hacker" :dir :system)

(set-state-ok t)

;;;Driver of the ACL2 executable
;;;Manages startup procedures and update loop
;;;Controls framerate via sleep.
;;;
;;;
;;;Note on debugging: the sleep* and get-internal-time
;;;functions use Common lisp features not usable in the ProofPad
;;;read-eval-print loop. If you want to debug main outside
;;;of a build, make sure these are replaced with do-nothing
;;;functions



;;sleep for the given seconds (time is a rational number)
(defun sleep* (time) 
  ; time)
   (sleep time))

;;get the time in milliseconds since startup
(defun get-runtime-ms ()
  ; 4)
   (* 1000 (/ (get-internal-run-time) internal-time-units-per-second)))

;;Sleep for a delay to keep the framerate on track
(defun adjust-framerate* (max-fps actual-ms-per-frame)
   (if (equal max-fps 0)
       t  ;should never be here
       (let* ((min-mspf (* 1000 (/ 1 max-fps)))
         	   (adjustment (/ (- min-mspf actual-ms-per-frame) 1000)))
             (if (< adjustment 0)
                 t ;already too slow
                 (sleep* adjustment)))))
                 
;;main update loop
(defun update* (world state)
  (if (cell-world-should-terminate world)
      state
      (let* ((t1 (get-runtime-ms)))
	      (mv-let (updated-world state)
	              (read-command-file "commands.txt" world
	                state)
                   (let* ((framerate (cell-world-framerate updated-world)))
                        (if (equal framerate 0)
                      	 ;Sleep a little and check for being unpaused.
                          (let* ((sl (sleep* 1/100)))
                                 (declare (ignore sl))
                                 (update* updated-world state))
                          ;;otherwise, run grid update loop
		                (let* ((grid-step-results (grid-step (cell-world-grid updated-world) 
		                                         (cell-world-rules updated-world)))
		                     (updated-grid (car grid-step-results))
		                     (changeset (cadr grid-step-results))
		                     (state (write-changes changeset 
		                      (ruleset-rule-type (cell-world-rules updated-world))
                          (cell-world-gen-num updated-world)
		                      state))
	                          (t2 (get-runtime-ms))   
                          	 (nonsense (adjust-framerate* (cell-world-framerate world) (- t2 t1))))
                                (declare (ignore nonsense))
			                     (update* (increment-world-gen-num (set-world-grid updated-world updated-grid))
			                             state))))))))

;;Adapted from write-all-strings in io-utilites.
;;Writes out string without appending a newline
(defun write-a-string (strli channel state)
 (princ$ strli channel state))

;;Adapted from string-list->file in io-utilities.
;;Like string-list->file, but writes just one string
;;WITHOUT appending a newline.
(defun string->file (fname str state)
  (mv-let (channel state)
          (open-output-channel fname :character state)
     (if (null channel)
         (mv (string-append "Error opening file for output: " fname)
             state)
            (let* ((state (write-a-string str channel state))
  				(state (close-output-channel channel state)))
              (mv nil state)))))

(defun pass-rules (rules state)
   (mv-let (err state)
           (string->file "ruletype.txt" (ruleset-rule-type rules) state);(ruleset-rule-type rules) state)
      	(declare (ignore err))
      	state))

;;Start the update loop
(defun run-cellular-automata* (rules state)
   (let* ((state (pass-rules rules state))
           (state (update* (construct-world rules) state)))
   state))
    
;;Read the FILENAME of the rules file to read in
;;from rules.txt. rules.txt contains the name of the
;;file to read
(defun read-rules-filename (state)
  (mv-let (name err state)
  	(file->string "rules.txt" state)
     (declare (ignore err))
     (mv name state)))

;;Read in the actual rule object and construct 
(defun read-rulefile (name state)
  (mv-let (rule-str err state)
    (file->string name state)
     (declare (ignore err))
    (mv (make-rules (str->chrs rule-str))
      state)))

;;This is called when the ACL2 executable is invoked.
(defun main (state) 
(mv-let (name state)
 (read-rules-filename state)
 (mv-let (ruleset state)
   (read-rulefile name state)
   (run-cellular-automata* ruleset state))))